package com.yifeng.repo.base.utils.codec;

import com.google.common.collect.Maps;
import org.springframework.util.DigestUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 接口签名工具类
 *
 * 适用场景：接口签名认证
 * @author maoyun
 * @version 1.0.0
 */
public class SignHelper {

    private static final String VERSION = "1.0.0";

    /**
     * 1、无请求体的签名参数验证
     * <br/>
     * 指定参数签名：timestamp精确到毫秒的时间戳、path调用接口路径
     * <br/>
     * 生成步骤：将待签字段用map装载后自然排序，外加secretKey拼接成长字符串，最后用MD5加密输出sign签名值
     * @param secretKey 签名密钥
     * @return
     */
    public static String createSign(Long timestamp, String path, String secretKey){
        return createSign(timestamp, path, VERSION, secretKey);
    }

    /**
     * 1、无请求体的签名参数验证
     * <br/>
     * 指定参数签名：timestamp精确到毫秒的时间戳、path调用接口路径
     * <br/>
     * 生成步骤：将待签字段用map装载后自然排序，外加secretKey拼接成长字符串，最后用MD5加密输出sign签名值
     * @param secretKey 签名密钥
     * @return
     */
    public static String createSign(Long timestamp, String path, String version, String secretKey){
        return commonSign(buildCommonMap(timestamp, path, version), secretKey);
    }

    /**
     * 2、有请求头、请求体的签名参数验证
     * <br/>
     * paramBodyMap 参数必须存储请求头、请求体全部信息，否则将签名认证失败
     * @param timestamp 时间戳
     * @param path 访问接口全路径
     * @param secretKey 签名密钥
     * @param paramBodyMap 请求头和请求体合并后的Map集合
     * @return
     */
    public static String createParamAndBodySign(Long timestamp, String path, String secretKey, Map<String, Object> paramBodyMap){
        if (paramBodyMap == null || paramBodyMap.isEmpty()){
            return createSign(timestamp, path, VERSION, secretKey);
        }
        // 构造公共签名参数
        Map<String, Object> commonMap = buildCommonMap(timestamp, path, VERSION);
        // 放入请求头请求体的签名参数
        commonMap.putAll(paramBodyMap);
        return commonSign(commonMap, secretKey);
    }

    private static Map<String, Object> buildCommonMap(Long timestamp, String path, String version) {
        Map<String, Object> map = Maps.newHashMap();
        // 值应该为毫秒数的字符串形式
        map.put("timestamp", timestamp.toString());
        map.put("path", path);
        map.put("version", version);
        return map;
    }
    private static String commonSign(Map<String, Object> signParamMap, String secretKey){
        List<String> storedKeys = Arrays.stream(signParamMap.keySet()
                        .toArray(new String[]{}))
                .sorted(Comparator.naturalOrder())
                .collect(Collectors.toList());
        String unsigned = storedKeys.stream()
                .map(key -> String.join("", key, Objects.toString(signParamMap.get(key), null)))
                .collect(Collectors.joining()).trim()
                .concat(secretKey);
        return DigestUtils.md5DigestAsHex(unsigned.getBytes()).toUpperCase();
    }
}
